﻿#include <string.h>
#include <stdarg.h>
#include <osa_mqueue.h>
#include <osa_mutex.h>

static OSA_MqdHndl posix_mq_list = NULL;
static OSA_MutexHndl posix_mq_lock;
int init = 0;

void posix_mq_system_init()
{
	OSA_mutexCreate(&posix_mq_lock);
}

/*inline*/ void posix_mq_insert(OSA_MqdHndl pmq)
{
	pmq->next = posix_mq_list;
	posix_mq_list = pmq;
}

static void posix_mq_delete(OSA_MqdHndl pmq)
{
	OSA_MqdHndl iter;
	if (posix_mq_list == pmq)
	{
		posix_mq_list = pmq->next;

		OSA_mqueDelete(pmq->mq);
		free(pmq);

		return;
	}
	for (iter = posix_mq_list; iter->next != NULL; iter = iter->next)
	{
		if (iter->next == pmq)
		{
			/* delete this mq */
			if (pmq->next != NULL)
				iter->next = pmq->next;
			else
				iter->next = NULL;

			/* delete RT-Thread mqueue */
			OSA_mqueDelete(pmq->mq);
			free(pmq);

			return;
		}
	}
}

static OSA_MqdHndl posix_mq_find(const char* name)
{
	OSA_MqdHndl iter;
	OSA_MqueHndl* object;

	for (iter = posix_mq_list; iter != NULL; iter = iter->next)
	{
		object = (OSA_MqueHndl*)(iter->mq);

		if (strncmp(object->name, name, 128) == 0)
		{
			return iter;
		}
	}

	return NULL;
}

int OSA_mqSetattr(OSA_MqdHndl                 mqdes,
	const struct osa_mq_attr *mqstat,
struct osa_mq_attr       *omqstat)
{

	return -1;
}

int OSA_mqGetattr(OSA_MqdHndl mqdes, struct osa_mq_attr *mqstat)
{
	if ((mqdes == NULL) || mqstat == NULL)
	{		
		return -1;
	}
	 
	mqstat->mq_maxmsg = mqdes->mq->len;
	mqstat->mq_msgsize = mqdes->mq->size;
	mqstat->mq_curmsgs = OSA_mqueGetQueuedCount(mqdes->mq);
	mqstat->mq_flags = 0;

	return 0;
}

OSA_MqdHndl OSA_mqOpen(const char *name, int oflag, ...)
{
	OSA_MqdHndl mqdes;
	va_list arg;
	int mode;
	struct osa_mq_attr *attr = NULL;

	if (!init) {
		posix_mq_system_init();
		init = 1;
	}

	/* lock posix mqueue list */
	OSA_mutexLock(&posix_mq_lock);

	mqdes = NULL;
	if (oflag & O_CREAT)
	{
		va_start(arg, oflag);
		mode = va_arg(arg, unsigned int);
		attr = (struct osa_mq_attr *)va_arg(arg, struct osa_mq_attr *);
		va_end(arg);

		if (oflag & O_EXCL)
		{
			if (posix_mq_find(name) != NULL)
			{
				goto __return;
			}
		}
		mqdes = (OSA_MqdHndl)malloc(sizeof(struct mqdes));
		if (mqdes == NULL)
		{
			goto __return;
		}

		mqdes->mq = (OSA_MqueHndl*)malloc(sizeof(OSA_MqueHndl));

		/* create message queue */
		OSA_mqueCreate(mqdes->mq, (char*)name, attr->mq_maxmsg, attr->mq_msgsize, (oflag & O_NONBLOCK) ? 0 : 1);
		if (mqdes->mq == NULL) /* create failed */
		{
			goto __return;
		}
		/* initialize reference count */
		mqdes->refcount = 1;
		mqdes->unlinked = 0;

		/* insert mq to posix mq list */
		posix_mq_insert(mqdes);
	}
	else
	{
		/* find mqueue */
		mqdes = posix_mq_find(name);
		if (mqdes != NULL)
		{
			mqdes->refcount++; /* increase reference count */
		}
		else
		{
			goto __return;
		}
	}
	OSA_mutexUnlock(&posix_mq_lock);

	return mqdes;

__return:
	/* release lock */
	OSA_mutexUnlock(&posix_mq_lock);

	/* release allocated memory */
	if (mqdes != NULL)
	{
		if (mqdes->mq != NULL)
		{
			/* delete RT-Thread message queue */
			OSA_mqueDelete(mqdes->mq);
		}
		free(mqdes);
	}
	return (OSA_MqdHndl)-1;
}

size_t OSA_mqReceive(OSA_MqdHndl mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio)
{
	size_t result;

	if ((mqdes == NULL) || (msg_ptr == NULL))
	{
		return -1;
	}

	result = OSA_mqueGet(mqdes->mq, msg_ptr, msg_len);
	if (result == 0)
		return msg_len;

	return -1;
}
 
int OSA_mqSend(OSA_MqdHndl mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio)
{
	size_t result;

	if ((mqdes == NULL) || (msg_ptr == NULL))
	{
		return -1;
	}

	result = OSA_mquePut(mqdes->mq, (char*)msg_ptr, msg_len);
	if (result == 0)
		return 0;

	return -1;
}

size_t OSA_mqTimedreceive(OSA_MqdHndl                  mqdes,
	char                  *msg_ptr,
	size_t                 msg_len,
	unsigned              *msg_prio,
	const struct timespec *abs_timeout)
{
	return -1;
}

int OSA_mqTimedsend(OSA_MqdHndl                  mqdes,
	const char            *msg_ptr,
	size_t                 msg_len,
	unsigned               msg_prio,
	const struct timespec *abs_timeout)
{
	return -1;
}

int OSA_mqNotify(OSA_MqdHndl mqdes, const struct sigevent *notification)
{
	return -1;
}

int OSA_mqClose(OSA_MqdHndl mqdes)
{
	if (mqdes == NULL)
	{
		return -1;
	}

	/* lock posix mqueue list */
	OSA_mutexLock(&posix_mq_lock);
	mqdes->refcount--;
	if (mqdes->refcount == 0)
	{
		/* delete from posix mqueue list */
		if (mqdes->unlinked)
			posix_mq_delete(mqdes);
	}
	OSA_mutexUnlock(&posix_mq_lock);

	return 0;
}

int OSA_mqUnlink(const char *name)
{
	OSA_MqdHndl pmq;

	/* lock posix mqueue list */
	OSA_mutexLock(&posix_mq_lock);
	pmq = posix_mq_find(name);
	if (pmq != NULL)
	{
		pmq->unlinked = 1;
		if (pmq->refcount == 0)
		{
			/* remove this mqueue */
			posix_mq_delete(pmq);
		}
		OSA_mutexUnlock(&posix_mq_lock);

		return 0;
	}
	OSA_mutexUnlock(&posix_mq_lock);

	return -1;
}

int OSA_mqDelete(OSA_MqdHndl hdl)
{
	return -1;
}
